package com.coocaa.x.framework.utils;

import android.content.Context;
import android.content.SharedPreferences;

import com.alibaba.fastjson.JSONObject;
import com.coocaa.x.framework.app.CoocaaApplication;
import com.coocaa.x.framework.data.JObject;
import com.duowan.mobile.netroid.Listener;
import com.duowan.mobile.netroid.NetroidError;
import com.duowan.mobile.netroid.Network;
import com.duowan.mobile.netroid.RequestQueue;
import com.duowan.mobile.netroid.cache.DiskCache;
import com.duowan.mobile.netroid.stack.HurlStack;
import com.duowan.mobile.netroid.toolbox.BasicNetwork;
import com.duowan.mobile.netroid.toolbox.FileDownloader;
import com.skyworth.framework.skysdk.util.MD5;

import org.apache.http.protocol.HTTP;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by lu on 15-9-2.
 */
public class Downloader {
    public static interface IDownloaderListener {
        void onStarted(DownloadTask task);

        void onPaused(DownloadTask task);

        void onCanceled(DownloadTask task);

        void onProgressUpdate(DownloadTask task, int percent, long fileSize, long downloadedSize);

        void onSuccess(DownloadTask task);

        void onError(DownloadTask task, int err_code, String err_msg);
    }

    public static final int EC_DOWNLOADER_SUCCESS = 0;
    public static final int EC_DOWNLOADER_TASK_NOT_EXIST = 1;
    public static final int EC_DOWNLOADER_TASK_NOT_START = 2;
    public static final int EC_DOWNLOADER_TASK_NOT_IN_DOWNLOADING = 3;

    private class MyListener extends Listener<Void> {

        private DownloadTask task = null;
        private List<IDownloaderListener> callbacks = null;

        public MyListener(DownloadTask task, List<IDownloaderListener> callbacks) {
            this.task = task;
            this.callbacks = callbacks;
        }

        @Override
        public void onPreExecute() {
            synchronized (callbacks) {
                for (final IDownloaderListener i : callbacks) {
                    CoocaaApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            i.onStarted(task);
                        }
                    });
                }
            }
        }

        @Override
        public void onError(final NetroidError error) {
            synchronized (callbacks) {
                for (final IDownloaderListener i : callbacks) {
                    CoocaaApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            if (error != null && error.networkResponse != null) {
                                i.onError(task, error.networkResponse.statusCode, error.networkResponse.charset);
                            } else {
                                i.onError(task, -100000, "unknow download error!");
                            }
                        }
                    });
                }
            }
        }

        @Override
        public void onCancel() {
            dcCache.remove(task._getID());
            taskCache.remove(task._getID());
            saveTaskList(taskCache.values());
            synchronized (callbacks) {
                for (final IDownloaderListener i : callbacks) {
                    CoocaaApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            i.onCanceled(task);
                        }
                    });
                }
            }
        }

        @Override
        public void onProgressChange(final long fileSize, final
        long downloadedSize) {
            final int percent = (int) (100 * downloadedSize / fileSize);
            synchronized (callbacks) {
                for (final IDownloaderListener i : callbacks) {
                    CoocaaApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            i.onProgressUpdate(task, percent, fileSize, downloadedSize);
                        }
                    });
                }
            }
        }

        @Override
        public void onSuccess(Void aVoid) {
            synchronized (callbacks) {
                for (final IDownloaderListener i : callbacks) {
                    CoocaaApplication.post(new Runnable() {
                        @Override
                        public void run() {
                            i.onSuccess(task);
                        }
                    });
                }
            }
        }
    }

    public static class DownloadTask extends JObject {
        public static final int STATUS_WAITING = 1;
        public static final int STATUS_DOWNLOADING = 2;
        public static final int STATUS_PAUSE = 3;
        public static final int STATUS_SUCCESS = 4;

        private String ID = null;
        public long create_time = System.currentTimeMillis();
        public String save_path, download_url;
        public String extra;

        public synchronized String _getID() {
            if (ID == null)
                ID = MD5.md5s(toJSONString());
            return ID;
        }
    }

    public static synchronized Downloader create(String name, int parallelCount) {
        return new Downloader(name, parallelCount);
    }


    private List<IDownloaderListener> callbacks = new ArrayList<IDownloaderListener>();

    public synchronized void addCallback(IDownloaderListener l) {
        synchronized (callbacks) {
            if (!callbacks.contains(l)) {
                callbacks.add(l);
            }
        }
    }

    public synchronized void removeCallback(IDownloaderListener l) {
        synchronized (callbacks) {
            if (callbacks.contains(l)) {
                callbacks.remove(l);
            }
        }
    }


    private static final String CONFIG_NAME = "downloader_%s";
    private static final String CONFIG_KEY = "downloader_tasklist";

    private Cache<String, DownloadTask> taskCache = new Cache<String, DownloadTask>();
    private Cache<String, FileDownloader.DownloadController> dcCache = new Cache<String, FileDownloader.DownloadController>();

    private FileDownloader filedownloader = null;
    private RequestQueue mQueue = null;


    private SharedPreferences sp = null;

    private String name = null;
    private int parallelCount = 1;


    private Downloader(String name, int parallelCount) {
        sp = CoocaaApplication.getContext().getSharedPreferences(String.format(CONFIG_NAME, name), Context.MODE_WORLD_READABLE
                + Context.MODE_WORLD_WRITEABLE);
        this.name = name;
        this.parallelCount = parallelCount;
    }

    public void init() {
        Network network = new BasicNetwork(new HurlStack("superx.cn", null), HTTP.UTF_8);
        mQueue = new RequestQueue(network, parallelCount + 1, new DiskCache(new File(CoocaaApplication.getContext().getCacheDir(), "cache")));
        mQueue.start();
        filedownloader = new FileDownloader(mQueue, parallelCount);
        DownloadTask[] list = getTaskList();
        if (list != null) {
            for (DownloadTask task : list)
                start(task);
        }
    }

    public synchronized int start(final DownloadTask task) {
        FileDownloader.DownloadController dc = null;
        int status = getStatus(task);
        if (status >= 0) {
            switch (status) {
                case DownloadTask.STATUS_PAUSE:
                    dc.resume();
                    return -EC_DOWNLOADER_SUCCESS;
                default:
                    return status;
            }
        }
        LiteHandlerThread.postOnUiThread(new Runnable() {
            @Override
            public void run() {
                FileDownloader.DownloadController _dc = filedownloader.add(task.save_path, task.download_url, new MyListener(task, callbacks));
                dcCache.add(task._getID(), _dc);
            }
        });
        taskCache.add(task._getID(), task);
        saveTaskList(taskCache.values());
        return -EC_DOWNLOADER_SUCCESS;
    }

    public synchronized int pause(DownloadTask task) {
        if (taskCache.containsKey(task._getID())) {
            FileDownloader.DownloadController dc = dcCache.get(task._getID());
            if (dc != null) {
                dc.pause();
                return -EC_DOWNLOADER_SUCCESS;
            }
            return -EC_DOWNLOADER_TASK_NOT_START;
        }
        return -EC_DOWNLOADER_TASK_NOT_EXIST;
    }

    public synchronized int cancel(DownloadTask task) {
        if (taskCache.containsKey(task._getID())) {
            FileDownloader.DownloadController dc = dcCache.get(task._getID());
            if (dc != null) {
                dc.discard();
                removeTask(task, true);
                return -EC_DOWNLOADER_SUCCESS;
            }
            return -EC_DOWNLOADER_TASK_NOT_START;
        }
        return -EC_DOWNLOADER_TASK_NOT_EXIST;
    }

    public synchronized int getStatus(DownloadTask task) {
        if (taskCache.containsKey(task._getID())) {
            FileDownloader.DownloadController dc = dcCache.get(task._getID());
            if (dc != null) {
                switch (dc.getStatus()) {
                    case FileDownloader.DownloadController.STATUS_WAITING:
                        return DownloadTask.STATUS_WAITING;
                    case FileDownloader.DownloadController.STATUS_DOWNLOADING:
                        return DownloadTask.STATUS_DOWNLOADING;
                    case FileDownloader.DownloadController.STATUS_PAUSE:
                        return DownloadTask.STATUS_PAUSE;
                    case FileDownloader.DownloadController.STATUS_SUCCESS:
                        return DownloadTask.STATUS_SUCCESS;
                    default:
                        return -EC_DOWNLOADER_TASK_NOT_EXIST;
                }
            }
            return -EC_DOWNLOADER_TASK_NOT_START;
        }
        return -EC_DOWNLOADER_TASK_NOT_EXIST;
    }

    public String getSavePath() {
        String dir = "/" + name + "_download";
        File file = CoocaaApplication.getContext().getExternalCacheDir();
        if (file != null && Storage.getFreeSpace(file.getAbsolutePath(), true) > 50 * 1024 * 1024) {
            file = new File(file.getAbsolutePath() + dir);
            try {
                Runtime.getRuntime().exec("chmod -R 777 " + CoocaaApplication.getContext().getExternalCacheDir().getAbsolutePath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            file = new File(CoocaaApplication.getContext().getCacheDir().getAbsolutePath() + dir);
            try {
                Runtime.getRuntime().exec("chmod -R 777 " + CoocaaApplication.getContext().getCacheDir().getAbsolutePath());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (!file.exists())
            file.mkdirs();
        try {
            Runtime.getRuntime().exec("chmod -R 777 " + file.getAbsolutePath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return file.getAbsolutePath();
    }


    private synchronized void removeTask(DownloadTask task, boolean removeFile) {
        dcCache.remove(task._getID());
        taskCache.remove(task._getID());
        saveTaskList(taskCache.values());
        if (removeFile) {
            File file = new File(task.save_path);
            if (file.exists())
                file.delete();
        }
    }

    private synchronized void saveTaskList(List<DownloadTask> list) {
        synchronized (sp) {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            SharedPreferences.Editor editor = sp.edit();
            editor.putString(CONFIG_KEY, JSONObject.toJSONString(list));
            editor.commit();
        }
    }

    private synchronized DownloadTask[] getTaskList() {
        synchronized (sp) {
            try {
                String value = sp.getString(CONFIG_KEY, "");
                if (value != null && !value.equals(""))
                    return (DownloadTask[]) JObject.parseJObject(value, DownloadTask[].class);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
}
